/**
 * 
 */
package qy.jalgotrade.strategy.position;

import java.time.ZonedDateTime;

import com.google.common.math.DoubleMath;

import qy.jalgotrade.broker.Order;
import qy.jalgotrade.broker.OrderEvent;
import qy.jalgotrade.utils.CommonUtils;

/**
 * @author c-geo
 *
 */
public class OpenState extends PositionState {

	/**
	 * 
	 */
	public OpenState() {

	}

	/* (non-Javadoc)
	 * @see qy.jalgotrade.strategy.position.PositionState#onEnter(qy.jalgotrade.strategy.position.Position)
	 */
	@Override
	public void onEnter(Position position) {

		ZonedDateTime entryDateTime = position.getEntryOrder().getExecutionInfo().getDateTime();
		position.setEntryDateTime(entryDateTime);
	}

	/* (non-Javadoc)
	 * @see qy.jalgotrade.strategy.position.PositionState#canSubmitOrder(qy.jalgotrade.strategy.position.Position, qy.jalgotrade.broker.Order)
	 */
	@Override
	public void canSubmitOrder(Position position, Order order) throws Exception {

		// Only exit orders should be submitted in this state.
	}

	/* (non-Javadoc)
	 * @see qy.jalgotrade.strategy.position.PositionState#onOrderEvent(qy.jalgotrade.strategy.position.Position, qy.jalgotrade.broker.OrderEvent)
	 */
	@Override
	public void onOrderEvent(Position position, OrderEvent orderEvent) throws Exception {

		if (position.getExitOrder() != null && position.getExitOrder().getId().equals(orderEvent.getOrder().getId())) {
			if (orderEvent.getEventType() == OrderEvent.Type.FILLED) {
				if (DoubleMath.fuzzyEquals(position.getShares(), 0, CommonUtils.FUZZY_EQUAL_TOLERANCE_MATH)) {
					position.switchState(new ClosedState());
					position.getStrategy().onExitOk(position);
				}
			} else if (orderEvent.getEventType() == OrderEvent.Type.CANCELED) {
				assert !DoubleMath.fuzzyEquals(position.getShares(), 0, CommonUtils.FUZZY_EQUAL_TOLERANCE_MATH);
				position.getStrategy().onExitCanceled(position);
			}
		} else if (position.getEntryOrder().getId().equals(orderEvent.getOrder().getId())) {
			// Nothing to do since the entry order may be completely filled or canceled after a partial fill.
			assert !DoubleMath.fuzzyEquals(position.getShares(), 0, CommonUtils.FUZZY_EQUAL_TOLERANCE_MATH);
		} else {
			throw new Exception(String.format("Invalid order event \"%s\" in OpenState", orderEvent.getEventType()));
		}
	}

	/* (non-Javadoc)
	 * @see qy.jalgotrade.strategy.position.PositionState#isOpen(qy.jalgotrade.strategy.position.Position)
	 */
	@Override
	public boolean isOpen(Position position) {

		return true;
	}

	/* (non-Javadoc)
	 * @see qy.jalgotrade.strategy.position.PositionState#exit(qy.jalgotrade.strategy.position.Position, double, double, boolean)
	 */
	@Override
	public void exit(Position position, double stopPrice, double limitPrice, boolean goodTillCanceled)
	        throws Exception {

		assert !DoubleMath.fuzzyEquals(position.getShares(), 0, CommonUtils.FUZZY_EQUAL_TOLERANCE_MATH);

		// Fail if a previous exit order is active.
		if (position.exitActive()) {
			throw new Exception("Exit order is active and it should be canceled first");
		}

		// If the entry order is active, request cancellation.
		if (position.entryActive()) {
			position.getStrategy().getBroker().cancelOrder(position.getEntryOrder());
		}

		position._submitExitOrder(stopPrice, limitPrice, goodTillCanceled);
	}
}
